<?php

/**
 * mForge Framework
 *
 * LICENSE:
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * The license is bundled with this package in the file LICENSE.txt.
 * You may also obtain a copy of the License at this URL:
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * @copyright  Copyright (c) 2011, Jonathan Hedrén
 * @author     Jonathan Hedrén <jonathan.hedren@gmail.com>
 * @license    http://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0
 */

namespace mForge\Modules\Authentication;

use mForge\IDependent;
use mForge\IModule;
use mForge\Application;
use mForge\Events;
use mForge\RedirectResult;
use mForge\Request;
use mForge\Response;
use mForge\RoutingContext;
use mForge\ValidationResult;
use mForge\ViewResult;
use mForge\Modules\FlashMessenger\Messenger;

require_once 'lib/Auth.php';
require_once 'lib/LoginModel.php';
require_once 'Helper.php';

class Module implements IModule, IDependent
{
    public static function getDependencies()
    {
        return array(
            'FlashMessenger',
            'Session',
            'Users'
        );
    }

    /**
     *
     * @var Auth
     */
    private $auth;

    /**
     *
     * @var Messenger
     */
    private $flashMessenger;

    /**
     *
     * @var array
     */
    private $moduleConfig;

    /**
     *
     * @var \mForge\Modules\Session\Module
     */
    private $sessionModule;

    /**
     *
     * @var \mForge\Modules\Users\Module
     */
    private $usersModule;

    /**
     *
     * @param Application $app
     */
    public function init(Request $request, Response $response, Application $app, array $moduleConfig)
    {
        $this->moduleConfig = $moduleConfig;

        $this->auth = $auth = new Auth(
            $this->usersModule->getUserService(),
            $this->sessionModule->getSession(),
            $moduleConfig['lifetime']
        );
        
        if (!$auth->isAuthenticated()) {
            $app->observeEvent(
                Application::ON_ROUTE_MATCH,
                function(RoutingContext $routingContext) use ($auth, $moduleConfig, $request) {

                    $currentRoute = $routingContext->getRoute();

                    if ($currentRoute == $moduleConfig['loginRoute']) {
                        return;
                    }

                    if ($moduleConfig['defaultIsAuthenticate']) {
                        foreach ($moduleConfig['routeAccess'] as $route => $access)
                        {
                            if ($currentRoute == $route && $access) {
                                return;
                            }
                        }
                        $request->redirectTo($moduleConfig['loginRoute']);

                    } else {
                        foreach ($moduleConfig['routeAccess'] as $route => $access)
                        {
                            if ($currentRoute == $route && !$access) {
                                $request->redirectTo($moduleConfig['loginRoute']);
                            }
                        }
                    }

                }
            );
        }
        
        Helper::setAuthenticationModule($this);

        $userService = $this->usersModule->getUserService();
        $flashMessenger = $this->flashMessenger;

        /**
         *
         */
        $app->route($moduleConfig['loginRoute'], function(LoginModel $model) use ($request, $auth) {
            if ($auth->isAuthenticated()) {
                return new RedirectActionResult($request->getApplicationPath());
            }
            return new ViewResult(array(
                'pageTitle' => 'Log in',
                'loginModel' => $model
            ));
        });

        /**
         * 
         */
        $app->route(
            $moduleConfig['loginRoute'],
            'post',
            function(LoginModel $model, ValidationResult $validationResult) use ($auth, $request, $userService, $moduleConfig, $flashMessenger) {

                if ($validationResult->isValid()) {
                
                    $user = $userService->findByUsername($model->username);
                    
                    if ($user != null && $user->isActive) {
                        if ($user->isLockedOut) {
                            $flashMessenger->addMessage('The account is locked. Please contact the system administrator.', Messenger::ERROR);
                            return new RedirectResult($moduleConfig['loginRoute']);
                        }

                        if ($auth->authenticate($model->password, $user->password)) {
                            $user->lastLoginDate = date('Y-m-d H:i:s');
                            $userService->save($user);

                            $flashMessenger->addMessage('You are logged in.');

                            return new RedirectResult($request->getApplicationPath());

                        } else {
                            $user->failedLoginAttempts++;
                            if ($user->failedLoginAttempts > $moduleConfig['numberOfMaxFailedLoginAttempts']) {
                                $user->isLockedOut = true;
                                $flashMessenger->addMessage('The account has been locked due to too many failed login attempts.', Messenger::ERROR);
                            }
                            $userService->save($user);
                        }
                    } else {
                        $flashMessenger->addMessage('Invalid username or password.', Messenger::ERROR);
                    }
                }
                return new ViewResult(array(
                    'pageTitle' => 'Log in',
                    'loginModel' => $model
                ));
            }
        );

        /**
         * 
         */
        $app->route(
            $moduleConfig['logoutRoute'],
            function() use ($auth, $moduleConfig, $flashMessenger) {
                $auth->clearAuthentication();
                $flashMessenger->addMessage('You have been logged out.');
                return new RedirectResult($moduleConfig['loginRoute']);
            }
        );
    }

    /**
     *
     * @return Auth
     */
    public function getAuth()
    {
        return $this->auth;
    }

    /**
     *
     * @return array
     */
    public function getConfig()
    {
        return $this->moduleConfig;
    }

    /**
     *
     * @param Messenger $flashMessengerModule
     */
    public function setFlashMessengerModule(IModule $flashMessengerModule)
    {
        $this->flashMessenger = $flashMessengerModule->getMessenger();
    }


    /**
     *
     * @param \mForge\Modules\Session\Module $sessionModule
     */
    public function setSessionModule(\mForge\Modules\Session\Module $sessionModule)
    {
        $this->sessionModule = $sessionModule;
    }

    /**
     *
     * @param \mForge\Modules\Users\Module $usersModule
     */
    public function setUsersModule(\mForge\Modules\Users\Module $usersModule)
    {
        $this->usersModule = $usersModule;
    }
}